"""
Kotlin 符号提取器

支持两种提取模式：
1. AST模式：使用 kotlinc 编译器进行语法分析（推荐）
2. 正则模式：使用正则表达式进行快速但不太精确的提取（回退方案）
"""

import re
import json
import subprocess
import xml.etree.ElementTree as ET
from typing import Dict, List, Optional, Tuple
from pathlib import Path

from core.extractors_module.base_extractor import LanguageExtractor
from core.interfaces_module.extractor import SymbolInfo


class KotlinExtractor(LanguageExtractor):
    """
    Kotlin 符号提取器

    支持:
    - 类 (class, data class, sealed class, enum class, inline class)
    - 接口 (interface)
    - 对象 (object, companion object)
    - 函数 (普通函数、扩展函数、内联函数、挂起函数)
    - 属性 (val/var, 委托属性)
    - 类型别名 (typealias)
    - 注解类 (annotation class)
    """

    def get_language_name(self) -> str:
        """获取语言名称"""
        return "kotlin"

    def get_compiler_command(self) -> str:
        """获取编译器命令"""
        return "kotlinc"

    def get_file_extensions(self) -> List[str]:
        """获取文件扩展名"""
        return ['.kt', '.kts']

    def _should_use_ast(self) -> bool:
        """
        判断是否应该使用 AST 提取

        Returns:
            如果可以且应该使用 AST 返回 True
        """
        # 检查是否有 kotlinc 编译器
        if not self.is_available():
            if self.config and self.config.analysis.require_ast:
                raise RuntimeError(
                    "需要 AST 提取但 kotlinc 编译器未找到。"
                    "请安装 Kotlin 编译器。"
                )
            return False

        # 如果配置要求 AST 或没有明确禁用，则使用 AST
        if self.config:
            if self.config.analysis.require_ast:
                return True
            if not self.config.analysis.allow_fallback:
                return True

        return self.is_available()  # 如果有编译器就用

    def extract_symbols_from_file(self, file_path: str) -> Dict:
        """
        从单个 Kotlin 文件提取符号

        优先使用 AST 提取，失败时降级到正则提取

        Args:
            file_path: 文件路径

        Returns:
            符号字典
        """
        # 尝试使用 AST 提取
        if self._should_use_ast():
            try:
                return self._extract_kotlin_ast(file_path)
            except Exception as e:
                if self.config and self.config.analysis.log_fallback:
                    self.logger.log_warning(
                        f"Kotlin AST 提取失败，降级到正则模式: {file_path}\n原因: {e}"
                    )
                if self.config and not self.config.analysis.allow_fallback:
                    raise

        # 降级到正则提取
        return self._extract_kotlin_regex(file_path)

    def _extract_kotlin_ast(self, file_path: str) -> Dict:
        """
        使用 kotlinc 提取 Kotlin 符号

        Kotlin 编译器提供多种分析选项：
        1. -Xprint-abi：输出 ABI 信息
        2. -Xdump-declarations：输出声明信息
        3. 使用 kotlin-compiler-embeddable API

        Args:
            file_path: Kotlin 文件路径

        Returns:
            符号字典
        """
        symbols = {
            'classes': {},
            'interfaces': {},
            'objects': {},
            'functions': {},
            'properties': {},
            'enums': {},
            'typealias': {},
            'annotations': {},
            'file_symbols': {file_path: []}
        }

        try:
            # 方案1：尝试使用 kotlinc 分析输出
            # kotlinc 可以输出各种中间信息
            cmd = [
                self.get_compiler_command(),
                '-progressive',  # 启用渐进模式
                '-Xprint-abi',  # 输出 ABI 信息
                '-d', '/dev/null',  # 不生成字节码
                file_path
            ]

            # 执行命令
            timeout = self.config.analysis.ast_timeout if self.config else 30
            result = subprocess.run(
                cmd,
                capture_output=True,
                text=True,
                timeout=timeout
            )

            # 如果上面的命令失败，尝试另一种方式
            if result.returncode != 0 or not result.stdout:
                # 方案2：使用语法分析模式
                cmd = [
                    self.get_compiler_command(),
                    '-no-stdlib',  # 不链接标准库
                    '-no-reflect',  # 不链接反射库
                    '-Werror',  # 将警告视为错误
                    '-d', '/dev/null',
                    file_path
                ]

                result = subprocess.run(
                    cmd,
                    capture_output=True,
                    text=True,
                    timeout=timeout
                )

            # 即使编译失败，错误信息中也包含了符号信息
            output = result.stdout + result.stderr

            if output:
                # 解析输出，提取符号信息
                self._parse_kotlin_output(output, file_path, symbols)

            # 如果输出信息不够，使用增强的正则分析
            if len(symbols['classes']) == 0 and len(symbols['functions']) == 0:
                # 回退到增强的正则分析
                return self._extract_kotlin_enhanced_regex(file_path)

        except subprocess.TimeoutExpired:
            raise Exception(f"Kotlin 分析超时 ({timeout}秒)")
        except Exception as e:
            raise Exception(f"Kotlin AST 提取失败: {e}")

        return symbols

    def _parse_kotlin_output(self, output: str, file_path: str, symbols: Dict):
        """
        解析 kotlinc 输出

        从编译器输出中提取符号信息

        Args:
            output: kotlinc 输出
            file_path: 源文件路径
            symbols: 符号字典
        """
        lines = output.split('\n')

        for line in lines:
            # 从错误信息中提取符号
            # 示例: "Unresolved reference: MyClass"
            if 'class ' in line or 'Class ' in line:
                match = re.search(r'class\s+(\w+)', line, re.IGNORECASE)
                if match:
                    class_name = match.group(1)
                    if class_name not in symbols['classes']:
                        symbols['classes'][class_name] = SymbolInfo(
                            name=class_name,
                            type='class',
                            file=file_path,
                            line=0,
                            column=0
                        )
                        symbols['file_symbols'][file_path].append(class_name)

            # 提取函数信息
            if 'fun ' in line or 'function ' in line:
                match = re.search(r'fun\s+(\w+)', line)
                if match:
                    func_name = match.group(1)
                    if func_name not in symbols['functions']:
                        symbols['functions'][func_name] = SymbolInfo(
                            name=func_name,
                            type='function',
                            file=file_path,
                            line=0,
                            column=0
                        )

    def _extract_kotlin_enhanced_regex(self, file_path: str) -> Dict:
        """
        使用增强的正则表达式提取 Kotlin 符号

        这是一个改进的正则提取，支持更多 Kotlin 特性

        Args:
            file_path: 文件路径

        Returns:
            符号字典
        """
        symbols = {
            'classes': {},
            'interfaces': {},
            'objects': {},
            'functions': {},
            'properties': {},
            'enums': {},
            'typealias': {},
            'annotations': {},
            'file_symbols': {file_path: []}
        }

        try:
            with open(file_path, 'r', encoding='utf-8') as f:
                content = f.read()

            # 移除注释
            content = self._remove_comments(content)

            # 使用更精确的正则表达式提取各种声明
            self._extract_classes(content, file_path, symbols)
            self._extract_interfaces(content, file_path, symbols)
            self._extract_objects(content, file_path, symbols)
            self._extract_functions(content, file_path, symbols)
            self._extract_properties(content, file_path, symbols)
            self._extract_enums(content, file_path, symbols)
            self._extract_typealias(content, file_path, symbols)
            self._extract_annotations(content, file_path, symbols)

        except Exception as e:
            self.logger.log_error(f"增强正则提取失败 {file_path}: {e}")

        return symbols

    def _extract_classes(self, content: str, file_path: str, symbols: Dict):
        """提取类声明"""
        # 支持各种类修饰符
        class_pattern = r'(?:(?:public|private|protected|internal)\s+)?(?:expect\s+)?(?:actual\s+)?(?:abstract\s+)?(?:final\s+)?(?:open\s+)?(?:sealed\s+)?(?:data\s+)?(?:inline\s+)?(?:value\s+)?(?:inner\s+)?class\s+(\w+)'

        for match in re.finditer(class_pattern, content):
            class_name = match.group(1)
            line_number = content[:match.start()].count('\n') + 1

            # 判断类的类型
            match_str = match.group(0)
            modifiers = []
            if 'data ' in match_str:
                modifiers.append('data')
            if 'sealed ' in match_str:
                modifiers.append('sealed')
            if 'abstract ' in match_str:
                modifiers.append('abstract')
            if 'open ' in match_str:
                modifiers.append('open')
            if 'inner ' in match_str:
                modifiers.append('inner')
            if 'inline ' in match_str or 'value ' in match_str:
                modifiers.append('inline')

            symbols['classes'][class_name] = SymbolInfo(
                name=class_name,
                type='class',
                file=file_path,
                line=line_number,
                column=0,
                modifiers=modifiers
            )
            symbols['file_symbols'][file_path].append(class_name)

    def _extract_interfaces(self, content: str, file_path: str, symbols: Dict):
        """提取接口声明"""
        interface_pattern = r'(?:(?:public|private|protected|internal)\s+)?(?:fun\s+)?interface\s+(\w+)'

        for match in re.finditer(interface_pattern, content):
            interface_name = match.group(1)
            line_number = content[:match.start()].count('\n') + 1

            symbols['interfaces'][interface_name] = SymbolInfo(
                name=interface_name,
                type='interface',
                file=file_path,
                line=line_number,
                column=0
            )
            symbols['file_symbols'][file_path].append(interface_name)

    def _extract_objects(self, content: str, file_path: str, symbols: Dict):
        """提取对象声明"""
        # 支持 object 和 companion object
        object_pattern = r'(?:(?:public|private|protected|internal)\s+)?(?:companion\s+)?object\s+(\w+)?'

        for match in re.finditer(object_pattern, content):
            object_name = match.group(1) if match.group(1) else 'Companion'
            line_number = content[:match.start()].count('\n') + 1

            modifiers = []
            if 'companion' in match.group(0):
                modifiers.append('companion')

            symbols['objects'][object_name] = SymbolInfo(
                name=object_name,
                type='object',
                file=file_path,
                line=line_number,
                column=0,
                modifiers=modifiers
            )
            symbols['file_symbols'][file_path].append(object_name)

    def _extract_functions(self, content: str, file_path: str, symbols: Dict):
        """提取函数声明"""
        # 支持各种函数类型
        # 1. 普通函数
        # 2. 扩展函数
        # 3. 内联函数
        # 4. 挂起函数
        # 5. 操作符函数
        function_pattern = r'(?:(?:public|private|protected|internal)\s+)?(?:expect\s+)?(?:actual\s+)?(?:inline\s+)?(?:noinline\s+)?(?:crossinline\s+)?(?:suspend\s+)?(?:operator\s+)?(?:infix\s+)?(?:override\s+)?(?:tailrec\s+)?fun\s+(?:<[^>]+>\s+)?(?:(\w+)\.)?(\w+)\s*\('

        for match in re.finditer(function_pattern, content):
            receiver = match.group(1)  # 扩展函数的接收者
            func_name = match.group(2)
            line_number = content[:match.start()].count('\n') + 1

            modifiers = []
            match_str = match.group(0)
            if 'inline ' in match_str:
                modifiers.append('inline')
            if 'suspend ' in match_str:
                modifiers.append('suspend')
            if 'operator ' in match_str:
                modifiers.append('operator')
            if 'infix ' in match_str:
                modifiers.append('infix')
            if receiver:
                modifiers.append(f'extension:{receiver}')

            symbols['functions'][func_name] = SymbolInfo(
                name=func_name,
                type='function',
                file=file_path,
                line=line_number,
                column=0,
                modifiers=modifiers
            )

    def _extract_properties(self, content: str, file_path: str, symbols: Dict):
        """提取属性声明"""
        # 支持 val/var，委托属性，lateinit 等
        property_pattern = r'(?:(?:public|private|protected|internal)\s+)?(?:override\s+)?(?:lateinit\s+)?(?:const\s+)?(?:val|var)\s+(?:<[^>]+>\s+)?(\w+)\s*(?::\s*([^=\n{]+))?(?:\s*(?:by|=))?'

        for match in re.finditer(property_pattern, content):
            prop_name = match.group(1)
            prop_type = match.group(2) if match.group(2) else None
            line_number = content[:match.start()].count('\n') + 1

            modifiers = []
            match_str = match.group(0)
            if 'val ' in match_str:
                modifiers.append('val')
            else:
                modifiers.append('var')
            if 'lateinit ' in match_str:
                modifiers.append('lateinit')
            if 'const ' in match_str:
                modifiers.append('const')
            if ' by ' in match_str:
                modifiers.append('delegated')

            symbols['properties'][prop_name] = SymbolInfo(
                name=prop_name,
                type='property',
                file=file_path,
                line=line_number,
                column=0,
                modifiers=modifiers,
                return_type=prop_type.strip() if prop_type else None
            )

    def _extract_enums(self, content: str, file_path: str, symbols: Dict):
        """提取枚举类声明"""
        enum_pattern = r'(?:(?:public|private|protected|internal)\s+)?enum\s+class\s+(\w+)'

        for match in re.finditer(enum_pattern, content):
            enum_name = match.group(1)
            line_number = content[:match.start()].count('\n') + 1

            symbols['enums'][enum_name] = SymbolInfo(
                name=enum_name,
                type='enum',
                file=file_path,
                line=line_number,
                column=0
            )
            symbols['file_symbols'][file_path].append(enum_name)

    def _extract_typealias(self, content: str, file_path: str, symbols: Dict):
        """提取类型别名声明"""
        typealias_pattern = r'(?:(?:public|private|protected|internal)\s+)?typealias\s+(\w+)\s*=\s*([^\n]+)'

        for match in re.finditer(typealias_pattern, content):
            alias_name = match.group(1)
            alias_type = match.group(2).strip()
            line_number = content[:match.start()].count('\n') + 1

            symbols['typealias'][alias_name] = SymbolInfo(
                name=alias_name,
                type='typealias',
                file=file_path,
                line=line_number,
                column=0,
                return_type=alias_type
            )
            symbols['file_symbols'][file_path].append(alias_name)

    def _extract_annotations(self, content: str, file_path: str, symbols: Dict):
        """提取注解类声明"""
        annotation_pattern = r'(?:(?:public|private|protected|internal)\s+)?annotation\s+class\s+(\w+)'

        for match in re.finditer(annotation_pattern, content):
            annotation_name = match.group(1)
            line_number = content[:match.start()].count('\n') + 1

            symbols['annotations'][annotation_name] = SymbolInfo(
                name=annotation_name,
                type='annotation',
                file=file_path,
                line=line_number,
                column=0
            )
            symbols['file_symbols'][file_path].append(annotation_name)

    def _extract_kotlin_regex(self, file_path: str) -> Dict:
        """
        使用基础正则表达式提取 Kotlin 符号（回退方案）

        Args:
            file_path: 文件路径

        Returns:
            符号字典
        """
        # 如果增强正则失败，使用基础版本
        symbols = {
            'classes': {},
            'interfaces': {},
            'objects': {},
            'functions': {},
            'properties': {},
            'enums': {},
            'typealias': {},
            'annotations': {},
            'file_symbols': {file_path: []}
        }

        try:
            with open(file_path, 'r', encoding='utf-8') as f:
                content = f.read()

            # 提取类声明（基础版）
            class_pattern = r'(?:data\s+)?(?:sealed\s+)?(?:open\s+)?(?:abstract\s+)?class\s+(\w+)'
            for match in re.finditer(class_pattern, content):
                class_name = match.group(1)
                line_number = content[:match.start()].count('\n') + 1

                symbols['classes'][class_name] = SymbolInfo(
                    name=class_name,
                    type='class',
                    file=file_path,
                    line=line_number,
                    column=0
                )
                symbols['file_symbols'][file_path].append(class_name)

            # 提取接口声明（基础版）
            interface_pattern = r'interface\s+(\w+)'
            for match in re.finditer(interface_pattern, content):
                interface_name = match.group(1)
                line_number = content[:match.start()].count('\n') + 1

                symbols['interfaces'][interface_name] = SymbolInfo(
                    name=interface_name,
                    type='interface',
                    file=file_path,
                    line=line_number,
                    column=0
                )
                symbols['file_symbols'][file_path].append(interface_name)

            # 提取对象声明（基础版）
            object_pattern = r'object\s+(\w+)'
            for match in re.finditer(object_pattern, content):
                object_name = match.group(1)
                line_number = content[:match.start()].count('\n') + 1

                symbols['objects'][object_name] = SymbolInfo(
                    name=object_name,
                    type='object',
                    file=file_path,
                    line=line_number,
                    column=0
                )
                symbols['file_symbols'][file_path].append(object_name)

            # 提取函数声明（基础版）
            function_pattern = r'fun\s+(\w+)\s*\('
            for match in re.finditer(function_pattern, content):
                func_name = match.group(1)
                line_number = content[:match.start()].count('\n') + 1

                symbols['functions'][func_name] = SymbolInfo(
                    name=func_name,
                    type='function',
                    file=file_path,
                    line=line_number,
                    column=0
                )

            # 提取属性声明（基础版）
            property_pattern = r'(?:val|var)\s+(\w+)\s*[:=]'
            for match in re.finditer(property_pattern, content):
                prop_name = match.group(1)
                line_number = content[:match.start()].count('\n') + 1

                symbols['properties'][prop_name] = SymbolInfo(
                    name=prop_name,
                    type='property',
                    file=file_path,
                    line=line_number,
                    column=0
                )

        except Exception as e:
            self.logger.log_error(f"解析 Kotlin 文件失败 {file_path}: {e}")

        return symbols

    def _remove_comments(self, content: str) -> str:
        """
        移除 Kotlin 代码中的注释

        Args:
            content: Kotlin 源代码

        Returns:
            移除注释后的代码
        """
        # 移除单行注释
        content = re.sub(r'//.*?$', '', content, flags=re.MULTILINE)
        # 移除多行注释
        content = re.sub(r'/\*.*?\*/', '', content, flags=re.DOTALL)
        # 移除文档注释
        content = re.sub(r'/\*\*.*?\*/', '', content, flags=re.DOTALL)
        return content

    def extract_symbols_batch(self, files: List[str]) -> Dict:
        """
        批量提取 Kotlin 符号

        优先使用 AST 模式，必要时降级到正则模式

        Args:
            files: Kotlin 文件列表

        Returns:
            合并后的符号字典
        """
        if not files:
            return {}

        mode = "AST" if self._should_use_ast() else "正则"
        self.logger.log_operation(f"Kotlin 分析 ({mode}模式)", f"处理 {len(files)} 个文件")

        symbols = {
            'classes': {},
            'interfaces': {},
            'objects': {},
            'functions': {},
            'properties': {},
            'enums': {},
            'typealias': {},
            'annotations': {},
            'file_symbols': {}
        }

        success_count = 0
        failed_files = []

        for file_path in files:
            try:
                file_symbols = self.extract_symbols_from_file(file_path)
                self._merge_symbols(symbols, file_symbols)
                success_count += 1
            except Exception as e:
                failed_files.append(file_path)
                self.logger.log_error(f"处理文件失败 {file_path}: {e}")

        if failed_files and self.config and self.config.analysis.log_fallback:
            self.logger.log_warning(
                f"Kotlin 提取完成：成功 {success_count}/{len(files)} 个文件\n"
                f"失败文件: {', '.join(failed_files[:5])}..."
            )

        return symbols

    def _merge_symbols(self, target: Dict, source: Dict):
        """
        合并符号字典

        Args:
            target: 目标字典
            source: 源字典
        """
        for key in source:
            if key == 'file_symbols':
                target[key].update(source[key])
            elif key in target:
                target[key].update(source[key])
            else:
                target[key] = source[key]